﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Core;
using Message;

namespace ServerBattle
{
    public class ConnectSystem : EntitySystem, IAwake, ILateUpdate
    {
        // 房间人数到多少后开启游戏
        private readonly int roomNum = 2;
        private int currentConnectRoomNum = 0;

        private Dictionary<uint, ISession> players = new Dictionary<uint, ISession>();

        private Stopwatch watch;
        public long lastTime;

        private bool isPlay;
        // 是否支持回放，支持回放会保留整局输入
        private bool isRecord;

        public readonly int perFrameTime = 40;
        public int currentSumTime;
        public float remain;

        // 服务器权威帧数据

        // 当前发送帧索引
        public int sendFrameIndex;
        // 当前服务帧索引
        public int serverFrameIndex;
        // 当前接收的玩家输入
        private Dictionary<int, Dictionary<uint, byte[]>> serverFrames = new Dictionary<int, Dictionary<uint, byte[]>>();

        public void Awake()
        {
            Subscribe<OnPlayerConnectRoom>(OnPlayerConnectRoom);
            Subscribe<OnDisconnectTcpServerCompleted>(OnDisconnectTcpServerCompleted);
            Subscribe<OnReceiveBattleCommand>(OnReceiveBattleCommand);

            Console.WriteLine($"设置房间人数:{roomNum}");
            Reset();
        }

        private void OnDisconnectTcpServerCompleted(ref OnDisconnectTcpServerCompleted data)
        {
            isPlay = false;

            uint userId = 0;
            var dataCom = data.session.Entity.GetComponent<PlayerDataComponent>();
            if (dataCom != null)
                userId = dataCom.userId;

            Console.WriteLine($"链接断开，SessionID: {data.session.SessionID}, userId:{userId}");
        }

        private void OnPlayerConnectRoom(ref OnPlayerConnectRoom data)
        {
            var dataCom = data.session.Entity.GetComponent<PlayerDataComponent>();
            players.Add(dataCom.userId, data.session);
            Console.WriteLine($"玩家准备, userId:{dataCom.userId}");

            currentConnectRoomNum++;
            if (currentConnectRoomNum == roomNum)
            {
                Console.WriteLine($"所有玩家准备完毕，通知启动战斗");

                StartBattle();
            }
        }

        // 重置数据准备下一把
        private void Reset()
        {
            currentConnectRoomNum = 0;
            players.Clear();
            watch = null;
            lastTime = 0;
            isPlay = false;
            isRecord = false;
            currentSumTime = 0;
            remain = 0;
            sendFrameIndex = 0;
            serverFrameIndex = 0;
            serverFrames.Clear();
        }

        private void StartBattle()
        {
            watch = new Stopwatch();
            watch.Start();
            lastTime = watch.ElapsedMilliseconds;

            isPlay = true;

            // 通知玩家游戏开始

            var push = new BattleStartPush();
            push.playerCount = roomNum;

            push.playerIds = new List<int>();
            foreach (var item in players)
                push.playerIds.Add((int)item.Key);

            SendToAllPlayer(push);
        }

        public void SendToAllPlayer(IMessage message)
        {
            foreach (var player in players.Values)
                player.SendMessage(message);
        }

        private void OnReceiveBattleCommand(ref OnReceiveBattleCommand data)
        {
            var command = data.command;

            var dataCom = data.session.Entity.GetComponent<PlayerDataComponent>();

            Console.WriteLine($"get:{command.tick}, userId:{dataCom.userId}, serverTick:{serverFrameIndex}, ms:{watch.ElapsedMilliseconds} {GetTime()}");

            // 响应玩家消息
            data.session.SendMessage(new BattleCommandPlayerPushTime()
            {
                tick = command.tick,
                serverFrameIndex = serverFrameIndex,
                currentSumTime = currentSumTime
            });

            // 客户端的data可能为null，发送过来是为了获取时间的，可以直接返回
            if (command.data == null)
                return;

            if (command.tick < serverFrameIndex)
            {
                Console.WriteLine($"收到过期数据，丢弃。");
                return;
            }

            if (!serverFrames.TryGetValue(command.tick, out var dic))
            {
                dic = new Dictionary<uint, byte[]>();
                serverFrames[command.tick] = dic;
            }

            if (dic.ContainsKey(dataCom.userId))
                Console.WriteLine($"数据已存在，替换帧数据, tick:{command.tick}, userId:{dataCom.userId}");

            dic[dataCom.userId] = command.data;
        }

        public void LateUpdate()
        {
            if (!isPlay)
                return;

            var offset = watch.ElapsedMilliseconds - lastTime;
            lastTime = watch.ElapsedMilliseconds;
            currentSumTime += (int)offset;

            if (currentSumTime >= perFrameTime)
            {
                Console.WriteLine($"Frame End:{serverFrameIndex}, currentSumTime:{currentSumTime}, {GetTime()} \n");

                currentSumTime -= perFrameTime;

                if (!serverFrames.TryGetValue(serverFrameIndex, out var frameData))
                {
                    frameData = new Dictionary<uint, byte[]>();
                    serverFrames[serverFrameIndex] = frameData;
                }

                if (frameData.Count == roomNum)
                {
                    // 如果在这一帧结束前所有玩家的输入都接收到了，可以尝试删除
                    TryRemoveFrameDataOnEnd();
                }
                else
                {
                    // 没有收到输入，添加一个空的
                    foreach (var item in players)
                    {
                        if (!frameData.ContainsKey(item.Key))
                        {
                            frameData[item.Key] = null;
                            Console.WriteLine($"miss======{serverFrameIndex}, id:{item.Key}");
                        }
                    }
                }

                serverFrameIndex++;
            }

            CheckSendData();
        }

        private void TryRemoveFrameDataOnEnd()
        {
            if (isRecord)
                return;

            if (sendFrameIndex <= serverFrameIndex)
                return;

            serverFrames.Remove(serverFrameIndex);
        }

        private void CheckSendData()
        {
            if (serverFrames.Count == 0)
                return;

            if (sendFrameIndex > serverFrameIndex)
                return;

            if (serverFrames.TryGetValue(sendFrameIndex, out var frameData) &&
                frameData.Count >= roomNum)
            {
                TryRemoveFrameDataOnSend();

                var tickCommand = new BattleCommandServerPush();
                tickCommand.tick = sendFrameIndex;

                if (frameData != null)
                {
                    tickCommand.datas = new List<byte[]>();
                    foreach (var item in frameData)
                    {
                        if (item.Value != null)
                            tickCommand.datas.Add(item.Value);
                    }
                }

                Console.WriteLine($"send serverTick:{sendFrameIndex}, {GetTime()}");
                SendToAllPlayer(tickCommand);

                sendFrameIndex++;
            }
        }

        private void TryRemoveFrameDataOnSend()
        {
            if (isRecord)
                return;

            if (sendFrameIndex == serverFrameIndex)
                return;

            serverFrames.Remove(sendFrameIndex);
        }

        private string GetTime()
        {
            var time = DateTime.Now;
            return $"time(s:{time.Second},m:{time.Millisecond})";
        }
    }
}